#include "ui_window.h"
#include "ui_std_ex.h"
#include "ui_painter.h"
#include "ui_application.h"
#include "ui_error.h"
#include "ui_message_server.h"
#include "ui_view.h"
#include <algorithm>

namespace UI
{

UI_Window::UI_Window(const UI_Rect& WindowRect, UI_Window* pParent) :
	m_WindowRect(WindowRect),
	m_ClientRect(WindowRect.SizeRect()),
	m_pParentWindow(NULL),
	m_pSDLSurface(NULL),
	m_bVisible(true),
	m_receive_event(true)
{
	if (!UI_Application::Instance())
	{
		throw(UI_Ex_App("CWindow Constructor: No Application instance!"));
	}

	SetWindowRect(WindowRect);
	SetNewParent(pParent);
}

UI_Window::~UI_Window(void)
{
	// Each child window is deleted, and should in their destructors call back to this object to Deregister themselves
	UI_MessageServer::Instance().DeregisterMessageClient(this);

	this->ClearSurface();
	if (m_pSDLSurface)
		SDL_FreeSurface(m_pSDLSurface);
	while (m_ChildWindows.size() > 0)
	{
		delete *(m_ChildWindows.begin());
	}
	m_ChildWindows.clear();
	SetNewParent(0);
}


void UI_Window::SetWindowRect(const UI_Rect& WindowRect)
{
	double dHorizontalScale = m_WindowRect.Width() != 0 ? static_cast<double>(WindowRect.Width()) / m_WindowRect.Width() : 0;
	double dVerticalScale = m_WindowRect.Height() != 0 ? static_cast<double>(WindowRect.Height()) / m_WindowRect.Height() : 0;
	m_WindowRect = WindowRect;
	m_ClientRect = UI_Rect(stdex::safe_static_cast<int>(m_ClientRect.Left() * dHorizontalScale), stdex::safe_static_cast<int>(m_ClientRect.Top() * dVerticalScale),
		stdex::safe_static_cast<int>(m_ClientRect.Right() * dHorizontalScale), stdex::safe_static_cast<int>(m_ClientRect.Bottom() * dVerticalScale));
	Draw();
}

void UI_Window::SetBackground()
{
	if (m_pSDLSurface)
			SDL_FreeSurface(m_pSDLSurface);
	if(!m_pSDLSurface)
		m_pSDLSurface = SDL_CreateRGBSurface(SDL_SWSURFACE, m_WindowRect.Width(), m_WindowRect.Height(),
				UI_Application::Instance()->GetBitsPerPixel(), 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000);
}

void UI_Window::SetBackground(SDL_Surface* p_surface, SDL_Rect& source)
{
	if(!p_surface)
		this->SetBackground();
	SDL_BlitSurface(p_surface,&source,this->m_pSDLSurface,NULL);
}

void UI_Window::SetBackground(const string& picfile)
{
	KTV_Draw* p_draw = UI_Application::Instance()->GetDrawTextObj();
	SDL_Surface* surface = p_draw->GetFromImageFile(picfile);
	UI_Rect rect(this->m_WindowRect.Left(),this->m_WindowRect.Top(),this->m_WindowRect.Left() + surface->w, this->m_WindowRect.Top() + surface->h);
	if(this->m_pSDLSurface)
		SDL_FreeSurface(this->m_pSDLSurface);
	this->m_pSDLSurface = surface;
	this->SetWindowRect(rect);
}


void UI_Window::MoveWindow(const UI_Point& MoveDistance)
{
	m_WindowRect = m_WindowRect + MoveDistance;
	UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::APP_PAINT, 0, this));
}


UI_Window* UI_Window::GetAncestor(EAncestor eAncestor) const
{
	UI_Window* pWindow = 0;

	switch (eAncestor)
	{
	case PARENT:
		pWindow = m_pParentWindow;
		break;
	case ROOT:
		pWindow = m_pParentWindow;
		if (pWindow)
		{
			while (pWindow->m_pParentWindow)
			{
				pWindow = pWindow->m_pParentWindow;
			}
		}
		else
		{
			pWindow = const_cast<UI_Window*>(this);
		}
		break;
	}

	return pWindow;
}

bool UI_Window::IsChildOf(UI_Window* pWindow) const
{
	const UI_Window* pCurrentWindow = this;
	bool bIsChild = false;

	while (!bIsChild && pCurrentWindow)
	{
		pCurrentWindow = pCurrentWindow->GetAncestor(PARENT);
		if (pCurrentWindow == pWindow)
		{
			bIsChild = true;
		}
	}

	return bIsChild;
}


void UI_Window::SetVisible(bool bVisible)
{
	if (m_bVisible != bVisible)
	{
		m_bVisible = bVisible;
		for (std::list<UI_Window*>::const_iterator iter = m_ChildWindows.begin(); iter != m_ChildWindows.end(); ++iter)
		{
			(*iter)->SetVisible(bVisible);
			if (!bVisible && (*iter) == UI_Application::Instance()->GetKeyFocus())
			{
				UI_Application::Instance()->SetKeyFocus(0);
			}
		}
		UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::APP_PAINT, 0, this));
	}
}


UI_Rect UI_Window::ClientToView(const UI_Rect& Rect) const
{
	UI_Rect ScreenRect(Rect + m_ClientRect.TopLeft() + m_WindowRect.TopLeft());
	if (m_pParentWindow)
	{
		ScreenRect = m_pParentWindow->ClientToView(ScreenRect);
	}
	return ScreenRect;
}


UI_Point UI_Window::ClientToView(const UI_Point& Point) const
{
	UI_Point ScreenPoint(Point + m_ClientRect.TopLeft() + m_WindowRect.TopLeft());
	if (m_pParentWindow)
	{
		ScreenPoint = m_pParentWindow->ClientToView(ScreenPoint);
	}
	return ScreenPoint;
}


UI_Rect UI_Window::ViewToClient(const UI_Rect& Rect) const
{
	UI_Rect WindowRect(Rect - m_WindowRect.TopLeft() - m_ClientRect.TopLeft());
	if (m_pParentWindow)
	{
		WindowRect = m_pParentWindow->ViewToClient(WindowRect);
	}
	return WindowRect;
}


UI_Point UI_Window::ViewToClient(const UI_Point& Point) const
{
	UI_Point WindowPoint(Point - m_WindowRect.TopLeft() - m_ClientRect.TopLeft());
	if (m_pParentWindow)
	{
		WindowPoint = m_pParentWindow->ViewToClient(WindowPoint);
	}
	return WindowPoint;
}


UI_Rect UI_Window::ViewToWindow(const UI_Rect& Rect) const
{
	UI_Rect WindowRect(Rect - m_WindowRect.TopLeft());
	if (m_pParentWindow)
	{
		WindowRect = m_pParentWindow->ViewToClient(WindowRect);
	}
	return WindowRect;
}


UI_Point UI_Window::ViewToWindow(const UI_Point& Point) const
{
	UI_Point WindowPoint(Point - m_WindowRect.TopLeft());
	if (m_pParentWindow)
	{
		WindowPoint = m_pParentWindow->ViewToClient(WindowPoint);
	}
	return WindowPoint;
}

bool UI_Window::HitTest(const UI_Point& Point) const
{
	bool bHit = m_WindowRect.SizeRect().HitTest(ViewToWindow(Point)) == UI_Rect::RELPOS_INSIDE;
	for (std::list<UI_Window*>::const_iterator iter = m_ChildWindows.begin(); !bHit && iter != m_ChildWindows.end(); ++iter)
	{
		bHit = (*iter)->HitTest(Point);
	}
	return bHit;
}


void UI_Window::Draw(void)
{
	UI_MessageServer::Instance().QueueMessage(new UI_Message(UI_Message::APP_PAINT, 0, this));
}

void UI_Window::DrawString(const string& text,const UI_Point& point,int size,const UI_Color& color,int style){
	if(text.size()){
		UI_Color c = color;
		if(size == 0) size = UI_Application::Instance()->GetDefaultFontSize();
		if(style == -1) style = UI_Application::Instance()->GetDefaultFontStyle();
		if(c == COLOR_SPECIAL) c = UI_Application::Instance()->GetDefaultFontColor();
		SDL_Surface* surf = UI_Application::Instance()->GetDrawTextObj()->DrawUTF8_Solid(text,c,size,style);
		DrawSurface(surf,point);
	}
	else{
		Draw();
	}
}
void UI_Window::DrawSurface(SDL_Surface* p_surface,const SDL_Rect& source,const SDL_Rect& dest){
	if(p_surface){
		m_ldrawsurface.push_back(new drawsurface_param(p_surface,source,dest));
		Draw();
	}
	else{
		std::cerr <<"DrawSurface Error" << std::endl;
	}
}
void UI_Window::DrawSurface(SDL_Surface* p_surface,const UI_Point& point){
	if(p_surface){
		SDL_Rect source = {0,0,p_surface->w,p_surface->h};
		SDL_Rect dest = {point.XPos(),point.YPos(),p_surface->w,p_surface->h};
		this->DrawSurface(p_surface,source,dest);
	}
	else{
		std::cerr <<"DrawSurface Error" << std::endl;
	}
}

void UI_Window::ClearSurface(void){
	for(std::list<drawsurface_param*>::iterator it = m_ldrawsurface.begin(); it != m_ldrawsurface.end();++ it){
		delete *it;
	}
	m_ldrawsurface.clear();
}

void UI_Window::PaintToSurface(SDL_Surface* ScreenSurface,const UI_Point& Offset)
{
	if (m_bVisible)
	{
		SDL_Rect SourceRect = UI_Rect(m_WindowRect.SizeRect()).SDLRect();
		SDL_Rect DestRect = UI_Rect(m_WindowRect + Offset).SDLRect();
		if(m_pSDLSurface){
			SDL_BlitSurface(m_pSDLSurface, &SourceRect, ScreenSurface, &DestRect);
		}
		UI_Point NewOffset = m_ClientRect.TopLeft() + m_WindowRect.TopLeft() + Offset;
		for (std::list<UI_Window*>::const_iterator iter = m_ChildWindows.begin(); iter != m_ChildWindows.end(); ++iter)
		{
			if (*iter)
			{
				(*iter)->PaintToSurface(ScreenSurface, NewOffset);
			}
		}
		if(!m_ldrawsurface.empty()){
			for(std::list<drawsurface_param*>::iterator it = m_ldrawsurface.begin(); it != m_ldrawsurface.end();++ it){
				UI_Rect dest((*it)->Dest_Rect);
				dest = dest + UI_Point(DestRect.x,DestRect.y);
				SDL_Rect r2 = dest.SDLRect();
				SDL_BlitSurface((*it)->Surface,&(*it)->Source_Rect,ScreenSurface,&r2);
			}
		}
	}
}


void UI_Window::SetNewParent(UI_Window* pNewParent,bool revEvent)
{
	if (m_pParentWindow)
	{
		m_pParentWindow->DeregisterChildWindow(this);
	}
	if (pNewParent)
	{
		pNewParent->RegisterChildWindow(this);
	}
	m_pParentWindow = pNewParent;
	m_receive_event = revEvent;
}


bool UI_Window::OnMouseButtonDown(UI_Point Point, unsigned int Button)
{
	bool bResult = false;

	if (m_bVisible && (m_WindowRect.SizeRect().HitTest(ViewToWindow(Point)) == UI_Rect::RELPOS_INSIDE))
	{
		for (std::list<UI_Window*>::reverse_iterator iter = m_ChildWindows.rbegin(); iter != m_ChildWindows.rend(); ++iter)
		{
			bResult = (*iter)->OnMouseButtonDown(Point, Button);
			if (bResult)
			{
				break;
			}
		}
	}

	return bResult;
}


bool UI_Window::OnMouseButtonUp(UI_Point Point, unsigned int Button)
{
	bool bResult = false;

	if (m_bVisible && (m_WindowRect.SizeRect().HitTest(ViewToWindow(Point)) == UI_Rect::RELPOS_INSIDE))
	{
		for (std::list<UI_Window*>::reverse_iterator iter = m_ChildWindows.rbegin(); iter != m_ChildWindows.rend(); ++iter)
		{
			bResult = (*iter)->OnMouseButtonUp(Point, Button);
			if (bResult)
			{
				break;
			}
		}
	}

	return bResult;
}


void UI_Window::RegisterChildWindow(UI_Window* pWindow)
{
	if (!pWindow)
	{
		// anything that gets registered should be a valid CWindow
		UI_Application::Instance()->GetApplicationLog().AddLogEntry(
			"CWindow::RegisterChildWindow : Attempting to register a non-existent child window.", APP_LOG_ERROR);
	}
	else
	{
		m_ChildWindows.push_back(pWindow);
	}
}


void UI_Window::DeregisterChildWindow(UI_Window* pWindow)
{
	m_ChildWindows.remove(pWindow);
}


bool UI_Window::HandleMessage(UI_Message* /*pMessage*/)
{
	bool bHandled = false;

	return bHandled;
}

}

